using System;
using System.Net.WebSockets;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using Newtonsoft.Json;

namespace touhou_bullet_rain;

public class LevelClient {

    private Level level;

    public string serverUri;
    private ClientWebSocket webSocket;
    // 客户端信息

    private Queue<LevelAction> interactBuffer;
    private Queue<LevelAction> actionsBuffer;
    // 消息缓存区

    public bool disconnected = false;

    public Queue<LevelAction> ActionsBuffer { get => actionsBuffer; set => actionsBuffer = value; }
    public Queue<LevelAction> InteractBuffer { get => interactBuffer; set => interactBuffer = value; }

    /// <summary>
    /// 全局缓冲区锁
    /// </summary>
    public object bufferLock = new object();

    public LevelClient(Level level) {
        this.level = level;
        interactBuffer = new Queue<LevelAction>();
        actionsBuffer = new Queue<LevelAction>();
        webSocket = new ClientWebSocket();
    }

    /// <summary>
    /// 向服务器询问并建立收发线程
    /// </summary>
    public async Task StartRequestTask(string serverUri) {
        this.serverUri = serverUri;
        try {
            await webSocket.ConnectAsync(new Uri(serverUri), CancellationToken.None);
        }
        catch (Exception e) {
            disconnected = true;
            Console.WriteLine("Error Occured when connecting to server -> " + e);
        }
        RequestGlobalSync();
        var tasks = new List<Task> {
            Task.Run(() => SendLevelData()),
            Task.Run(() => ReceiveLevelData())
        };
        // 等待所有任务完成，但不会阻塞当前线程
        try {
            await Task.WhenAll(tasks);
        }
        catch {
            Disconnect();
        }
    }

    /// <summary>
    /// 发送玩家创建请求
    /// </summary>
    public void RequestCreateNewPlayer(Player player) {
        AddInteractToBuffer("c", new string[4] { player.uuid, player.playerName, player.pos.X.ToString(), player.pos.Y.ToString() } );
    }

    /// <summary>
    /// 将输入信息加入传出缓冲区
    /// </summary>
    public void AddInteractToBuffer(string actionName, string[] paramsList) {
        lock (bufferLock) {
            LevelAction levelAction = new LevelAction(actionName, paramsList);
            interactBuffer.Enqueue(levelAction);
        }
    }

    /// <summary>
    /// 发送消息
    /// </summary>
    private async void SendLevelData() {
        while (webSocket.State == WebSocketState.Open) {
            try {
                while (interactBuffer.Count > 0) {
                    LevelAction action;
                    lock (bufferLock) {
                        action = interactBuffer.Dequeue();
                    }
                    string actionJson = JsonConvert.SerializeObject(action);
                    await SendLevelActionData(actionJson);
                }
                await Task.Delay(10);
            }
            catch {
                Console.WriteLine("Unable to Send Level Data.");
                Disconnect();
                break;
            }
        }
    }

    private async Task SendLevelActionData(string data) {
        try {
            if (webSocket.State == WebSocketState.Open) {
                byte[] bytes = Encoding.UTF8.GetBytes(data);
                await webSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None);
            }
        }
        catch {
            Console.WriteLine("Unable to Send Level Action Data.");
            Disconnect();
            return;
        }
    }

    /// <summary>
    /// 收取消息
    /// </summary>
    private async void ReceiveLevelData() {
        byte[] buffer = new byte[4096];
        while (webSocket.State == WebSocketState.Open) {
            try {
                var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                if (result.MessageType == WebSocketMessageType.Close) {
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
                }
                else if (result.MessageType == WebSocketMessageType.Text) {
                    string data = Encoding.UTF8.GetString(buffer, 0, result.Count);
                    LevelAction action = JsonConvert.DeserializeObject<LevelAction>(data);
                    lock (bufferLock) {
                        actionsBuffer.Enqueue(action);
                    }
                }
            }
            catch {
                Console.WriteLine("Unable to Receive Level Data.");
                Disconnect();
                break;
            }
        }
    }

    /// <summary>
    /// 从服务器断开连接
    /// </summary>
    public async void Disconnect() {
        try {
            if (webSocket != null && webSocket.State == WebSocketState.Open) {
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
                webSocket.Dispose();
            }
        }
        catch { Console.WriteLine("Unable To Disconnect From Server"); }
        Console.WriteLine("Disconnect From Server");
        disconnected = true;
    }

    /// <summary>
    /// 申请全局同步
    /// </summary>
    public void RequestGlobalSync() {
        AddInteractToBuffer("s", new string[0]);
        // 终于，我可以定义客户端请求全量状态了
    }

    /// <summary>
    /// 清空缓冲区
    /// </summary>
    public void ClearBuffer() {
        interactBuffer.Clear();
        actionsBuffer.Clear();
    }
}

